﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace monitor_server_2_WPF
{
    /// <summary>
    /// Логика взаимодействия для CoordinateSetter.xaml
    /// </summary>
    public partial class CoordinateSetter
    {
        public enum Destinations
        {
            North,
            South,
            West,
            East
        }

        private readonly List<Destinations> _availableDestinations;
        private Destinations _curDest;
        private string _curValue = "00000000";
        private readonly MenuItem _menuDestItem;

        public delegate void DataChangedHandler(string coords);
        public event DataChangedHandler DataChangedEvent;

        protected virtual void OnDataChangedEvent(string coords)
        {
            var handler = DataChangedEvent;
            if (handler != null) handler(coords);
        }


        public CoordinateSetter()
        {
            InitializeComponent();
            DestinationButton.Content = " ";
            DestinationsMenu.Items.Add(new MenuItem { Header = " " });
            _menuDestItem = (MenuItem)DestinationsMenu.Items[0];
            _availableDestinations = new List<Destinations>(4);
            DegreeBox.DataChangedEvent += BoxOnDataChangedEvent;
            MinuteBox.DataChangedEvent += BoxOnDataChangedEvent;
            SecondBox.DataChangedEvent += BoxOnDataChangedEvent;
        }

        public CoordinateSetter(Destinations availableDestination)
        {
            ChangeCurrentDestination(availableDestination);
        }

        public CoordinateSetter(List<Destinations> list)
        {
            _availableDestinations = list;
            DestinationButton.Content = DestinationsMenuItem.GetItem(list[0]).GetHead();
        }

        private void BoxOnDataChangedEvent(object sender, TextChangedEventArgs textChangedEventArgs)
        {
            UpdateCurValue();
            OnDataChangedEvent(Value);
        }

        private void UpdateCurValue()
        {
            var degrees = DegreeBox.Value.ToString(CultureInfo.InvariantCulture);
            var minutes = MinuteBox.Value.ToString(CultureInfo.InvariantCulture);
            var seconds = SecondBox.Value.ToString(CultureInfo.InvariantCulture);
            var sb = new StringBuilder();
            for (int i = degrees.Length; i < 3; i++)
            {
                sb.Append("0");
            }
            sb.Append(degrees);
            for (int i = minutes.Length; i < 2; i++)
            {
                sb.Append("0");
            }
            sb.Append(minutes);
            for (int i = seconds.Length; i < 2; i++)
            {
                sb.Append("0");
            }
            sb.Append(seconds);
            sb.Append(ParseDestination(_curDest));
            _curValue = sb.ToString();
        }

        public string Value
        {
            get
            {
                return _curValue;
            }
            set
            {
                int a;
                if (!int.TryParse(value.Remove(value.Length - 1, 1), out a))
                    throw new WrongCoordinatesException("Can't parse to integer value");
                if(value.Length > 8)
                    throw new WrongCoordinatesException("String is too long");
                if (value.Length < 8)
                {
                    var lastSymbol = value[value.Length - 1];
                    if (lastSymbol != 'N' && lastSymbol != 'S' && lastSymbol != 'W' && lastSymbol != 'E')
                        if(_availableDestinations.Count > 0 && _availableDestinations.Contains(_curDest))
                            value += ParseDestination(_curDest);
                        else
                        {
                            throw new WrongCoordinatesException("Destination " + _curDest + " is not available");
                        }
                    else
                    {
                        _curDest = ParseDestination(lastSymbol);
                    }
                    var sb = new StringBuilder(8);
                    for (int i = 0; i < 8 - value.Length; i++)
                        sb.Append("0");
                    sb.Append(value);
                    _curValue = sb.ToString();
                }
                else
                {
                    _curValue = value;
                }
                UpdateBoxValues(_curValue);
            }
        }

        private void UpdateBoxValues(string newValue)
        {
            DegreeBox.Value = int.Parse(newValue.Remove(3));
            MinuteBox.Value = int.Parse(newValue[3].ToString(CultureInfo.InvariantCulture) + newValue[4]);
            SecondBox.Value = int.Parse(newValue[5].ToString(CultureInfo.InvariantCulture) + newValue[6]);
            var lastSymbol = newValue[7];
            if (lastSymbol != '0')
                SetCurrentDestination(ParseDestination(lastSymbol));
        }

        public void SetCurrentDestination(Destinations dest)
        {
            if(!_availableDestinations.Contains(dest))
                throw new WrongCoordinatesException("Destination " + Res.Rm.GetString(dest.ToString() + " is not available"));
            DestinationButton.Content = DestinationsMenuItem.GetItem(dest).GetHead();
        }

        public void AddAvailableDestination(Destinations dest)
        {
            if (!_availableDestinations.Contains(dest))
            {
                _availableDestinations.Add(dest);
                var mi = new MenuItem {Header = DestinationsMenuItem.GetItem(dest)};
                mi.Click += DestinationMenuItemClicked;
                _menuDestItem.Items.Add(mi);
                if (DestinationButton.Content.Equals(" "))
                {
                    var item = ((DestinationsMenuItem) mi.Header);
                    DestinationButton.Content = item.GetHead();
                    ChangeCurrentDestination(item.GetDestination());
                }
            }
        }

        public void RemoveAvailableDestination(Destinations dest)
        {
            if (_availableDestinations.Contains(dest))
            {
                var head = DestinationsMenuItem.GetItem(dest).ToString();
                _availableDestinations.Remove(dest);
                int i = _menuDestItem.Items.Cast<MenuItem>().TakeWhile(item => !((DestinationsMenuItem) (item).Header).ToString().Equals(head)).Count();
                _menuDestItem.Items.RemoveAt(i);
                if(((TextBlock) DestinationButton.Content).Text.Equals(head))
                    if (_availableDestinations.Count > 0)
                    {
                        DestinationButton.Content = DestinationsMenuItem.GetItem(_availableDestinations[0]).GetHead();
                    }
                    else
                    {
                        DestinationButton.Content = " ";
                    }
            }
            
        }

        private void DestinationMenuItemClicked(object sender, RoutedEventArgs e)
        {
            var item = ((DestinationsMenuItem) ((MenuItem) sender).Header);
            DestinationButton.Content = item.GetHead();
            ChangeCurrentDestination(item.GetDestination());
        }

        private void ChangeCurrentDestination(Destinations dest)
        {
            _curDest = dest;
            _curValue = _curValue.Remove(7, 1);
            _curValue += ParseDestination(_curDest);
        }

        private Destinations ParseDestination(char d)
        {
            switch (d)
            {
                case 'N':
                    return Destinations.North;
                case 'S':
                    return Destinations.South;
                case 'W':
                    return Destinations.West;
                case 'E':
                    return Destinations.East;
            }
            throw new Exception(d + " can not be parsed to destination");
        }

        private char ParseDestination(Destinations dest)
        {
            switch (dest)
            {
                case Destinations.North:
                    return 'N';
                case Destinations.South:
                    return 'S';
                case Destinations.West:
                    return 'W';
                case Destinations.East:
                    return 'E';
            }
            throw new Exception("parsing destination error");
        }

        class WrongCoordinatesException : Exception
        {
            public WrongCoordinatesException(string message) : base(message)
            {
            }
        }

        class DestinationsMenuItem : DockPanel
        {
            private string _header;
            private readonly Destinations _destination;
            private Color _backGround;
            private readonly TextBlock _tb;
            private readonly TextBlock _info;

            public static DestinationsMenuItem GetItem(Destinations dest)
            {
                return new DestinationsMenuItem(dest);
            }

            private DestinationsMenuItem(Destinations dest)
            {
                _destination = dest;
                VerticalAlignment = VerticalAlignment.Center;
                HorizontalAlignment = HorizontalAlignment.Left;

                SetDestination(dest);
                _tb = CreateHeadBlock();
                _info = CreateInfoTextBlock();

                Children.Add(_tb);
                Children.Add(_info);
                
            }

            private TextBlock CreateHeadBlock()
            {
                var tb = new TextBlock
                    {
                        Height = 16,
                        Width = 16,
                        VerticalAlignment = VerticalAlignment.Center,
                        HorizontalAlignment = HorizontalAlignment.Center,
                        TextAlignment = TextAlignment.Center,
                        Background = new SolidColorBrush(_backGround),
                        Text = _header
                    };
                return tb;
            }

            private TextBlock CreateInfoTextBlock()
            {
                var info = new TextBlock
                    {
                        Margin = new Thickness(5, 0, 0, 0),
                        Text = Res.Rm.GetString(_destination.ToString())
                    };
                return info;
            }

            public UIElement GetHead()
            {
                return CreateHeadBlock();
            }

            public Destinations GetDestination()
            {
                return _destination;
            }

            private void SetDestination(Destinations dest)
            {
                switch (dest)
                {
                    case Destinations.North:
                        _header = "N";
                        _backGround = Colors.LightSkyBlue;
                        break;
                    case Destinations.South:
                        _header = "S";
                        _backGround = Colors.Gold;
                        break;
                    case Destinations.West:
                        _header = "W";
                        _backGround = Colors.Lime;
                        break;
                    case Destinations.East:
                        _header = "E";
                        _backGround = Colors.Violet;
                        break;
                }
            }

            public new string ToString()
            {
                return _header;
            }
        }

        private void DestinationButton_OnClick(object sender, RoutedEventArgs e)
        {
            if(_availableDestinations.Count > 0)
                ((MenuItem) DestinationsMenu.Items[0]).IsSubmenuOpen = true;
        }
    }
}
